#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "IPFuncRes.h"

void load_img() {

	int nColors;
	unsigned long fileSize;
	unsigned char *pChar, someChar;
	unsigned int row, col;
	char pathToFile[100];
	char fileToPath[100];
	someChar = '0';
	pChar = &someChar;


	printf("Path to file: ");
	scanf("%s", pathToFile);
	printf("File to path: ");
	scanf("%s", fileToPath);

	/*-------DECLARE INPUT & OUTPUT FILES-------*/
	bmpInput = fopen(pathToFile, "rb");
	bmpOutput = fopen(fileToPath, "wb");

	/*---SET POINTER TO BEGINNING OF FILE----*/
	fseek(bmpInput, 0L, SEEK_END);

	/*-------GET INPUT BMP DATA--------*/
	fileSize = getImageInfo(bmpInput, 2, 4);
	originalImage.cols = (int) getImageInfo(bmpInput, 18, 4);
	originalImage.rows = (int) getImageInfo(bmpInput, 22, 4);
	edgeImage.rows = originalImage.rows;
	edgeImage.cols = originalImage.cols;

	/*--------PRINT DATA TO SCREEN----------*/
	printf("Width: %d\n", originalImage.cols);
	printf("Height: %d\n", originalImage.rows);
	printf("File size: %lu\n", fileSize);

	nColors = (int) getImageInfo(bmpInput, 46, 4);
	printf("nColors: %d\n", nColors);

	/*------ALLOCATE MEMORY FOR FILES--------*/
	vectorSize = fileSize - (14 + 40 + 4 * nColors);
	printf("vectorSize: %lu\n", vectorSize);
	edgeImage.data = malloc(vectorSize * sizeof(unsigned char));
	if (edgeImage.data == NULL) {
		printf("Failed to malloc edgeImage.data\n");
		exit(0);
	}
	printf("%lu bytes malloc'ed for edgeImage.data\n", vectorSize);

	originalImage.data = malloc(vectorSize * sizeof(unsigned char));
	if (originalImage.data == NULL) {
		printf("Failed to malloc originalImage.data\n");
		exit(0);
	}
	printf("%lu bytes malloc'ed for originalImage.data\n", vectorSize);

	/*------COPY HEADER AND COLOR TABLE---------*/
	copyImageInfo(bmpInput, bmpOutput);
	copyColorTable(bmpInput, bmpOutput, nColors);
	fseek(bmpInput, (14 + 40 + 4 * nColors), SEEK_SET);
	fseek(bmpOutput, (14 + 40 + 4 * nColors), SEEK_SET);

	/* Read input.bmp and store it's raster data into originalImage.data */
	for (row = 0; row <= originalImage.rows - 1; row++) {
		for (col = 0; col <= originalImage.cols - 1; col++) {
			fread(pChar, sizeof(char), 1, bmpInput);
			*(originalImage.data + row * originalImage.cols + col) = *pChar;
		}
	}
}

void histograma(sImage originalImage, unsigned long vectorSize) {

	STATISTICA stat;
	int j, i = 0, Y, X;
	int indice = 0;
	int *hist;
	int faixa[8] = { 0 };
	FILE * arq;

	/* abrindo um arquivo */
	arq = fopen("teste.txt", "a");

	/* pegando os valores de 0 - 255 de cada pixel */
	hist = malloc(vectorSize * sizeof(int));
	for (Y = 0; Y <= (originalImage.rows - 1); Y++) {
		for (X = 0; X <= (originalImage.cols - 1); X++) {
			indice = (int) (*(originalImage.data + X + Y * originalImage.cols));
			hist[i++] = indice;
		}
	}

	/* separando esses valores por cada faixa */
	for (i = 0; i < vectorSize; i++) {
		if (hist[i] <= 31)
			faixa[0]++;
		if ((hist[i] >= 32) && (hist[i] <= 63))
			faixa[1]++;
		if ((hist[i] >= 64) && (hist[i] <= 95))
			faixa[2]++;
		if ((hist[i] >= 96) && (hist[i] <= 127))
			faixa[3]++;
		if ((hist[i] >= 128) && (hist[i] <= 159))
			faixa[4]++;
		if ((hist[i] >= 160) && (hist[i] <= 191))
			faixa[5]++;
		if ((hist[i] >= 192) && (hist[i] <= 224))
			faixa[6]++;
		if ((hist[i] >= 225) && (hist[i] <= 255))
			faixa[7]++;
	}
	/* para cada 1048 pixel de cada faixa vale uma estrela */
	faixa[0] = faixa[0] / 1048;
	faixa[1] = faixa[1] / 1048;
	faixa[2] = faixa[2] / 1048;
	faixa[3] = faixa[3] / 1048;
	faixa[4] = faixa[4] / 1048;
	faixa[5] = faixa[5] / 1048;
	faixa[6] = faixa[6] / 1048;
	faixa[7] = faixa[7] / 1048;

	/* quardando todos os valores para dentro da estrutura STATICA */
	for (j = 0; j < 8; j++) {
		if (faixa[j] != 0) {
			stat.statistica[j] = malloc(faixa[j] * sizeof(char));
			for (i = 0; i < faixa[j]; i++) {
				(stat.statistica[j])[i] = (char) '*';
			}
			(stat.statistica[j])[--i] = '\0';
		} else {
			stat.statistica[j] = malloc(sizeof(char));
			(stat.statistica[j])[0] = '\0';
		}
	}

	/* imprimir em tela e em arquivo */
	printf(" 0 - 31 de Intensidade: %s\n", stat.statistica[0]);
	fputs(" 0 - 31 de Intensidade:", arq);
	fputs(stat.statistica[0], arq);
	fputs("\n", arq);

	printf("32- 63 de Intensidade: %s\n", stat.statistica[1]);
	fputs("32- 63 de Intensidade:", arq);
	fputs(stat.statistica[1], arq);
	fputs("\n", arq);

	printf("64 - 95 de Intensidade: %s\n", stat.statistica[2]);
	fputs("64 - 95 de Intensidade:", arq);
	fputs(stat.statistica[2], arq);
	fputs("\n", arq);

	printf("96 - 127 de Intensidade: %s\n", stat.statistica[3]);
	fputs("96 - 127 de Intensidade:", arq);
	fputs(stat.statistica[3], arq);
	fputs("\n", arq);

	printf("128 - 159 de Intensidade: %s\n", stat.statistica[4]);
	fputs("128 - 159 de Intensidade:", arq);
	fputs(stat.statistica[4], arq);
	fputs("\n", arq);

	printf("160 - 191 de Intensidade: %s\n", stat.statistica[5]);
	fputs("160 - 191 de Intensidade:", arq);
	fputs(stat.statistica[5], arq);
	fputs("\n", arq);

	printf("192 - 224 de Intensidade: %s\n", stat.statistica[6]);
	fputs("192 - 224 de Intensidade:", arq);
	fputs(stat.statistica[6], arq);
	fputs("\n", arq);

	printf("225 - 255 de Intensidade: %s\n", stat.statistica[7]);
	fputs("225 - 255 de Intensidade:", arq);
	fputs(stat.statistica[7], arq);
	fputs("\n", arq);

	/* fechar o arquivo */
	fclose(arq);
}

int compare(const void * a, const void * b) {
	return (*(int*) a - *(int*) b);
}

void sobel(sImage originalImage, sImage edgeImage, FILE *bmpOutput) {
	sImage edgeImage1 = edgeImage;
	unsigned int X, Y;
	int I, J, SUM;
	long sumX, sumY;
	int GX[3][3];
	int GY[3][3];

	/* 3x3 GX Sobel mask.  Ref: www.cee.hw.ac.uk/hipr/html/sobel.html */
	GX[0][0] = -1;
	GX[0][1] = 0;
	GX[0][2] = 1;
	GX[1][0] = -2;
	GX[1][1] = 0;
	GX[1][2] = 2;
	GX[2][0] = -1;
	GX[2][1] = 0;
	GX[2][2] = 1;

	/* 3x3 GY Sobel mask.  Ref: www.cee.hw.ac.uk/hipr/html/sobel.html */
	GY[0][0] = 1;
	GY[0][1] = 2;
	GY[0][2] = 1;
	GY[1][0] = 0;
	GY[1][1] = 0;
	GY[1][2] = 0;
	GY[2][0] = -1;
	GY[2][1] = -2;
	GY[2][2] = -1;

	for (Y = 0; Y <= (originalImage.rows - 1); Y++) {
		for (X = 0; X <= (originalImage.cols - 1); X++) {
			sumX = 0;
			sumY = 0;

			/* image boundaries */
			if (Y == 0 || Y == originalImage.rows - 1)
				SUM = 0;
			else if (X == 0 || X == originalImage.cols - 1)
				SUM = 0;

			/* Convolution starts here */
			else {

				/*-------X GRADIENT APPROXIMATION------*/
				for (I = -1; I <= 1; I++) {
					for (J = -1; J <= 1; J++) {
						sumX = sumX
								+ (int) ((*(originalImage.data + X + I
										+ (Y + J) * originalImage.cols))
										* GX[I + 1][J + 1]);
					}
				}

				/*-------Y GRADIENT APPROXIMATION-------*/
				for (I = -1; I <= 1; I++) {
					for (J = -1; J <= 1; J++) {
						sumY = sumY
								+ (int) ((*(originalImage.data + X + I
										+ (Y + J) * originalImage.cols))
										* GY[I + 1][J + 1]);
					}
				}

				/*---GRADIENT MAGNITUDE APPROXIMATION (Myler p.218)----*/
				SUM = abs(sumX) + abs(sumY);
			}
			if (SUM > 255)
				SUM = 255;
			if (SUM < 0)
				SUM = 0;
			*(edgeImage1.data + X + Y * originalImage.cols) = 255
					- (unsigned char) (SUM);
			fwrite((edgeImage1.data + X + Y * originalImage.cols), sizeof(char),
					1, bmpOutput);
		}

	}
	fclose(bmpOutput);
}

void oper_min(sImage originalImage, sImage edgeImage, FILE *bmpOutput) {
	sImage edgeImage1 = edgeImage;
	unsigned int X, Y, pixel;
	int I, J, vetor[9], k = 0;

	for (Y = 0; Y <= (originalImage.rows - 1); Y++) {
		for (X = 0; X <= (originalImage.cols - 1); X++) {
			pixel = 0;


			/* image boundaries */
			if (Y == 0 || Y == originalImage.rows - 1)
				pixel = 0;
			else if (X == 0 || X == originalImage.cols - 1)
				pixel = 0;

			/* Convolution starts here */
			else {

				/*------GRADIENT APPROXIMATION------*/
				for (I = -1; I <= 1; I++) {
					for (J = -1; J <= 1; J++) {
						pixel = (int) ((*(originalImage.data + X + I
										+ (Y + J) * originalImage.cols)));

						vetor[k] = pixel;
						k++;
					}
				}
				k = 0;
				qsort(vetor, 9, sizeof(int), compare);
				pixel = vetor[0];

			}
			if (pixel > 255)
				pixel = 255;
			if (pixel < 0)
				pixel = 0;
			*(edgeImage1.data + X + Y * originalImage.cols) = (unsigned char) (pixel);
			fwrite((edgeImage1.data + X + Y * originalImage.cols), sizeof(char),
					1, bmpOutput);
		}
	}
}

void oper_med(sImage originalImage, sImage edgeImage, FILE *bmpOutput) {
	sImage edgeImage1 = edgeImage;
	unsigned int X, Y, pixel;
	int I, J, vetor[9], k = 0;

		for (Y = 0; Y <= (originalImage.rows - 1); Y++) {
		for (X = 0; X <= (originalImage.cols - 1); X++) {
			pixel = 0;


			/* image boundaries */
			if (Y == 0 || Y == originalImage.rows - 1)
				pixel = 0;
			else if (X == 0 || X == originalImage.cols - 1)
				pixel = 0;

			/* Convolution starts here */
			else {

				/*-------GRADIENT APPROXIMATION------*/
				for (I = -1; I <= 1; I++) {
					for (J = -1; J <= 1; J++) {
						pixel = (int) ((*(originalImage.data + X + I
										+ (Y + J) * originalImage.cols)));

						vetor[k] = pixel;
						k++;
					}
				}
				k = 0;
				qsort(vetor, 9, sizeof(int), compare);
				pixel = vetor[3];


			}
			if (pixel > 255)
				pixel = 255;
			if (pixel < 0)
				pixel = 0;
			*(edgeImage1.data + X + Y * originalImage.cols) = (unsigned char) (pixel);
			fwrite((edgeImage1.data + X + Y * originalImage.cols), sizeof(char),
					1, bmpOutput);
		}
	}
}

void oper_max(sImage originalImage, sImage edgeImage, FILE *bmpOutput) {
	sImage edgeImage1 = edgeImage;
	unsigned int X, Y,pixel;
	int I, J, vetor[9], k = 0;



	for (Y = 0; Y <= (originalImage.rows - 1); Y++) {
		for (X = 0; X <= (originalImage.cols - 1); X++) {
			pixel = 0;


			/* image boundaries */
			if (Y == 0 || Y == originalImage.rows - 1)
				pixel = 0;
			else if (X == 0 || X == originalImage.cols - 1)
				pixel = 0;
			/* Convolution starts here */
			else {

				/*------- GRADIENT APPROXIMATION------*/
				for (I = -1; I <= 1; I++) {
					for (J = -1; J <= 1; J++) {
						pixel = (int) ((*(originalImage.data + X + I
										+ (Y + J) * originalImage.cols)));

						vetor[k] = pixel;
						k++;
					}
				}
				k = 0;
				qsort(vetor, 9, sizeof(int), compare);
				pixel = vetor[8];


			}

			if (pixel > 255)
				pixel = 255;
			if (pixel < 0)
				pixel = 0;

			*(edgeImage1.data + X + Y * originalImage.cols) = (unsigned char) (pixel);
			fwrite((edgeImage1.data + X + Y * originalImage.cols), sizeof(char),
					1, bmpOutput);
		}
	}
}

void smoot(sImage originalImage, sImage edgeImage, FILE *bmpOutput) {
	sImage edgeImage1 = edgeImage;
	unsigned int X, Y, i = 0;
	int I, J, vetor[9], k = 0, SUM;
	float GX2[3][3];
	float GY2[3][3];
	long sumX, sumY;
	printf("1 - Smoothing Normal\n");
	printf("2 - Smoothing com Peso\n");
	fflush(stdin);
	scanf("%d", &i);
	fflush(stdin);
	if (i == 1) {

		/* SMOOTHNING*/
		GX2[0][0] = 1 / 9.0;
		GX2[0][1] = 1 / 9.0;
		GX2[0][2] = 1 / 9.0;
		GX2[1][0] = 1 / 9.0;
		GX2[1][1] = 1 / 9.0;
		GX2[1][2] = 1 / 9.0;
		GX2[2][0] = 1 / 9.0;
		GX2[2][1] = 1 / 9.0;
		GX2[2][2] = 1 / 9.0;

		GY2[0][0] = 1 / 9.0;
		GY2[0][1] = 1 / 9.0;
		GY2[0][2] = 1 / 9.0;
		GY2[1][0] = 1 / 9.0;
		GY2[1][1] = 1 / 9.0;
		GY2[1][2] = 1 / 9.0;
		GY2[2][0] = 1 / 9.0;
		GY2[2][1] = 1 / 9.0;
		GY2[2][2] = 1 / 9.0;
	} else {

		/* SMOOTHNING COM PESO*/
		GX2[0][0] = 1 / 9.0;
		GX2[0][1] = 2 / 16.0;
		GX2[0][2] = 1 / 16.0;
		GX2[1][0] = 2 / 9.0;
		GX2[1][1] = 4 / 16.0;
		GX2[1][2] = 2 / 16.0;
		GX2[2][0] = 1 / 9.0;
		GX2[2][1] = 2 / 16.0;
		GX2[2][2] = 1 / 16.0;

		GY2[0][0] = 1 / 16.0;
		GY2[0][1] = 2 / 16.0;
		GY2[0][2] = 1 / 16.0;
		GY2[1][0] = 2 / 16.0;
		GY2[1][1] = 4 / 16.0;
		GY2[1][2] = 2 / 16.0;
		GY2[2][0] = 1 / 16.0;
		GY2[2][1] = 2.0 / 9.0;
		GY2[2][2] = 1 / 16.0;
	}

	for (Y = 0; Y <= (originalImage.rows - 1); Y++) {
		for (X = 0; X <= (originalImage.cols - 1); X++) {
			sumX = 0;
			sumY = 0;

			/* image boundaries */
			if (Y == 0 || Y == originalImage.rows - 1)
				SUM = 0;
			else if (X == 0 || X == originalImage.cols - 1)
				SUM = 0;

			/* Convolution starts here */
			else {

				/*-------X GRADIENT APPROXIMATION------*/
				for (I = -1; I <= 1; I++) {
					for (J = -1; J <= 1; J++) {
						sumX = sumX
								+ (int) ((*(originalImage.data + X + I
										+ (Y + J) * originalImage.cols))
										* GX2[I + 1][J + 1]);
						vetor[k] = sumX;
						k++;
					}
				}
				k = 0;
				qsort(vetor, 9, sizeof(int), compare);
				sumX = vetor[8];

				/*-------Y GRADIENT APPROXIMATION-------*/
				for (I = -1; I <= 1; I++) {
					for (J = -1; J <= 1; J++) {
						sumY = sumY
								+ (int) ((*(originalImage.data + X + I
										+ (Y + J) * originalImage.cols))
										* GY2[I + 1][J + 1]);
						vetor[k] = sumY;
						k++;
					}
				}
				k = 0;
				qsort(vetor, 9, sizeof(int), compare);
				sumY = vetor[8];

				/*---GRADIENT MAGNITUDE APPROXIMATION (Myler p.218)----*/
				SUM = abs(sumX) + abs(sumY);
			}
			if (SUM > 255)
				SUM = 255;
			if (SUM < 0)
				SUM = 0;

			*(edgeImage1.data + X + Y * originalImage.cols) = (unsigned char) (SUM);
			fwrite((edgeImage1.data + X + Y * originalImage.cols), sizeof(char),
					1, bmpOutput);
		}
	}
}

void intensidade(sImage originalImage, sImage edgeImage, FILE *bmpOutput){
	unsigned int X, Y;
	float i = 0;
	float indice = 0;
	printf("digite a porcentagem da intensidade\n");
	scanf("%f", & i);
	fflush(stdin);
	i = i/100;
	for (Y = 0; Y <= (originalImage.rows - 1); Y++) {
		for (X = 0; X <= (originalImage.cols - 1); X++) {
			indice = (float) (*(originalImage.data + X + Y * originalImage.cols));
			//fflush(stdin);
			indice = indice * i;
			if (indice > 255)
				indice = 255;
			if (indice < 0)
				indice = 0;
			//fflush(stdin);
			*(edgeImage.data + X + Y * originalImage.cols) = 255 - indice ;
			fwrite((edgeImage.data + X + Y * originalImage.cols), sizeof(char),
					1, bmpOutput);
		}
	}
}

void contraste(sImage originalImage, sImage edgeImage, FILE *bmpOutput){
	unsigned int X, Y;

	float indice = 0;

	for (Y = 0; Y <= (originalImage.rows - 1); Y++) {
		for (X = 0; X <= (originalImage.cols - 1); X++) {
			indice = (float) (*(originalImage.data + X + Y * originalImage.cols));
			fflush(stdin);
			if (indice > 255)
				indice = 255;
			if (indice < 0)
				indice = 0;
			fflush(stdin);
			if(indice < 127){
				indice = 0;
				fflush(stdin);
			}else{
				indice = 500;
				fflush(stdin);
			}

			/*if(indice <= 51) indice = 25;
			if(indice >= 52 && indice <= 102) indice = 77;
			if(indice >= 103 && indice <= 153) indice = 128;
			if(indice >= 154 && indice <= 204) indice = 179;
			if(indice >= 205) indice = 230;*/
			//fflush(stdin);
			*(edgeImage.data + X + Y * originalImage.cols) = (unsigned char) (indice) ;
			fflush(stdin);
			fwrite((edgeImage.data + X + Y * originalImage.cols), sizeof(char),
					1, bmpOutput);
		}
	}
}

void copia(sImage originalImage, sImage edgeImage, FILE *bmpOutput){
	unsigned int X, Y;

	float indice = 0;
	for (Y = 0; Y <= (originalImage.rows - 1); Y++) {
		for (X = 0; X <= (originalImage.cols - 1); X++) {
			indice = (float) (*(originalImage.data + X + Y * originalImage.cols));
			if (indice > 255)
				indice = 255;
			if (indice < 0)
				indice = 0;
			*(edgeImage.data + X + Y * originalImage.cols) = indice ;
			fwrite((edgeImage.data + X + Y * originalImage.cols), sizeof(char),
					1, bmpOutput);
		}
	}
}



/*====================CONTROL MENU=================================*/
int menu(int i) {
	printf("1  - Carregar imagem\n");
	printf("2  - Aplicar Smoothing\n");
	printf("3  - Criar Histograma\n");
	printf("4  - Aplicar Max\n");
	printf("5  - Aplicar Med\n");
	printf("6  - Aplicar Min\n");
	printf("7  - Aplicar Sobel\n");
	printf("8  - Aplicar Intensidade\n");
	printf("9  - Copiar imagem\n");
	printf("10 - Aplicar Contraste\n");
	printf("11 - Pegar o rumo do beco\n");

	printf("Escolha uma opcao: ");
	scanf("%d", &i);
	return i;
}

/*====================GET IMAGE INFO SUBPROGRAM====================*/
long getImageInfo(FILE* inputFile, long offset, int numberOfChars) {
	unsigned char *ptrC;
	long value = 0L;
	unsigned char dummy;
	int i;

	dummy = '0';
	ptrC = &dummy;

	fseek(inputFile, offset, SEEK_SET);

	for (i = 1; i <= numberOfChars; i++) {
		fread(ptrC, sizeof(char), 1, inputFile);
		/* calculate value based on adding bytes */
		value = (long) (value + (*ptrC) * (pow(256, (i - 1))));
	}
	return (value);
} /* end of getImageInfo */

/*====================COPIES HEADER AND INFO HEADER================*/
void copyImageInfo(FILE* inputFile, FILE* outputFile) {
	unsigned char *ptrC;
	unsigned char dummy;
	int i;

	dummy = '0';
	ptrC = &dummy;

	fseek(inputFile, 0L, SEEK_SET);
	fseek(outputFile, 0L, SEEK_SET);

	for (i = 0; i <= 50; i++) {
		fread(ptrC, sizeof(char), 1, inputFile);
		fwrite(ptrC, sizeof(char), 1, outputFile);
	}
}

/*====================COPIES COLOR TABLE==========================-*/
void copyColorTable(FILE* inputFile, FILE* outputFile, int nColors) {
	unsigned char *ptrC;
	unsigned char dummy;
	int i;

	dummy = '0';
	ptrC = &dummy;

	fseek(inputFile, 54L, SEEK_SET);
	fseek(outputFile, 54L, SEEK_SET);

	for (i = 0; i <= (4 * nColors); i++) /* there are (4*nColors) bytesin color table */
	{
		fread(ptrC, sizeof(char), 1, inputFile);
		fwrite(ptrC, sizeof(char), 1, outputFile);
	}
}
